package xyz.tanxiao.links.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xyz.tanxiao.links.common.NetworkUtils;
import xyz.tanxiao.links.common.enums.*;
import xyz.tanxiao.links.common.exception.ToShortLinkException;
import xyz.tanxiao.links.common.request.ToShortLinkRequest;
import xyz.tanxiao.links.config.GlobalProperties;
import xyz.tanxiao.links.core.cache.LayeringCacheOperations;
import xyz.tanxiao.links.core.mapping.LongLinkMappingKey;
import xyz.tanxiao.links.entity.SlDomainManage;
import xyz.tanxiao.links.entity.SlLinkMapping;
import xyz.tanxiao.links.mapper.SlLinkMappingMapper;
import xyz.tanxiao.links.service.SlDomainManageService;
import xyz.tanxiao.links.service.SlLinkMappingService;

import java.time.LocalDateTime;
import java.util.List;

@Slf4j
@RequiredArgsConstructor
@Service
public class SlLinkMappingServiceImpl extends ServiceImpl<SlLinkMappingMapper, SlLinkMapping> implements SlLinkMappingService {

	private final GlobalProperties globalProperties;

	private final LayeringCacheOperations layeringCacheOperations;

	private final LongLinkMappingKey<String> linkMappingKey;

	private final SlDomainManageService domainManageService;


	/**
	 * 长链接转短链接
	 *
	 * @param request 请求参数
	 * @return 短链接
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public String toShortLink(ToShortLinkRequest request) {
		String sourceUrl = request.getSourceUrl();
		if (!(sourceUrl.startsWith("http://") || sourceUrl.startsWith("https://"))) {
			throw new ToShortLinkException("生成失败，请输入 http:// 或 https:// 开头的链接。");
		}

		// 检查转换的主机域名
		this.checkSourceDomain(sourceUrl);
		// 短链域名
		String domainName = this.globalProperties.getDomainName();
		// 生成一个不重复的映射Key
		String mappingKey = getNoRepeatMappingKey(sourceUrl);

		// 封装长链接映射信息
		SlLinkMapping newMapping = new SlLinkMapping();
		newMapping.setMappingKey(mappingKey);
		newMapping.setDomainName(domainName);
		newMapping.setLongLink(sourceUrl);
		newMapping.setStatus(MappingKeyStatusEnum.NORMAL.getStatus());
		newMapping.setEnableTime(request.getEnableTime());
		newMapping.setDisableTime(request.getDisableTime());
		newMapping.setAllowTotal(request.getAllowTotal());
		newMapping.setAccessPassword(request.getAccessPassword());
		newMapping.setMappingWay(MappingWayEnum.MD5_BASE62.getMappingWay());
		newMapping.setCreateTime(LocalDateTime.now());
		newMapping.setRequestIp(NetworkUtils.getRequestIp());
		this.save(newMapping);

		// 映射信息添加Redis缓存
		String cacheKey = LinksCacheKeyEnum.SHORT_LINKS_CACHE_KEY.getCacheKey() + mappingKey;
		this.layeringCacheOperations.set(cacheKey, newMapping);
		return domainName + mappingKey;
	}


	/**
	 * 检查转换的主机域名
	 *
	 * @param sourceUrl 原始URL
	 */
	@Override
	public void checkSourceDomain(String sourceUrl) {
		if (this.globalProperties.isEnableCheckDomain()) {
			return;
		}
		// 域名检查
		List<SlDomainManage> domains = this.domainManageService.getDomainsByStatus(CommonStatusEnum.ENABLE);
		for (SlDomainManage domain : domains) {
			if (sourceUrl.startsWith(domain.getDomainName())) {
				if (DomainTypeEnum.PROTECTION.getType() == domain.getType()) {
					throw new ToShortLinkException("生成失败，此链接域名为系统保护域名。");
				} else {
					throw new ToShortLinkException("生成失败，此链接域名已被禁用无法转换。");
				}
			}
		}
	}


	/**
	 * 生成一个不重复的映射Key
	 *
	 * @param longLink 长链接
	 * @return 映射Key
	 */
	@Override
	public String getNoRepeatMappingKey(String longLink) {
		for (String mappingKey : this.linkMappingKey.toMappingKey(longLink)) {
			long mappingKeyCount = this.count(
				Wrappers.lambdaQuery(SlLinkMapping.class).eq(SlLinkMapping::getMappingKey, mappingKey)
			);
			if (mappingKeyCount < 1) {
				return mappingKey;
			}
		}
		return this.getNoRepeatMappingKey(longLink);
	}

}